Solidity Gas Saving Tips
written by tak
Table of contents
1. Objectives
- Reduce spending of user
- Increase our audit competitiveness
2. Basic knowledge about gas
- Transaction cost and execution cost
- Opcode and gas cost mapping.
3. Explain tips
Tips
1. Avoid unnecessary event emission
Basically, logging opcodes are costly. Plus, charged additional cost.
The logging opcode is different from arguments. If only one argument are taken, the gas cost is 750. If two arguments are taken, the gas cost is 1125.
Additionally, other factors lead to additional charges, like index value or data size.
Let's compare gas cost between two function, one function have event and the other don't have event.
The execution gas cost of event having function is 1409. The other one is just 326.
2. Convert byte[] to bytesX
You should use bytes over byte[] because it is cheaper, since byte[] adds 31 padding bytes between the elements.
Always use one of the value types bytes1 to bytes32 because they are much cheaper.
3. Change string to byteX if possible
string is equal to bytes so that you can use bytesX as alternative of string. The max of byte is 32. So, If the string length is less than 32, you can use bytes type. It's very efficient.
Please see bellow comparison. When I convert "hello world!" to bytex32, nearly 3 times chipper than using string.
shuntak.icon < これはstackが32バイト単位で、PUSH1~32の命令が全て3Gas消費で同じということからも納得
moonty_sal.icon < because of the wordsize of EVM is 32 byte (so this likely reason for PUSH1 - PUSH32 has same gas consumption)
4. External function is efficient if argument is large?
Solidity official site state like this.
External functions are sometimes more efficient when they receive large arrays of data.
I cannot make this happen when I pass 320 byte of data. The gas cost was exact same between public function and external function
5. Pack struct tightly
When using elements that are smaller than 32 bytes, your contract’s gas usage may be higher. This is because the EVM operates on 32 bytes at a time. Therefore, if the element is smaller than that, the EVM must use more operations in order to reduce the size of the element from 32 bytes to the desired size.
Ensure that you try to order your storage variables and struct membersn such that they can be packed tightly.
Please see bellow, Unpacked case is nearly 30,000 higher than packed case.
yudetamago.icon > greater than 32 bytes?
mosa_siru.icon < this is the topic of packing elements within 32bytes, so "smaller".
yudetamago.icon > gocha, more accurately, smaller than or equal to ? Yes!
6. assert is big eater
The assert-style exceptions consume all gas available to the call. Please see the bellow simple example. When I pass a and b are zero with gas limit = 3000000, the execution cost is 2978472!.
On the other hand, require-style exceptions will not consume any gas starting from the Metropolis release.
The execution cost is only 382.
7. Function order affect gas consumption
The order of function will also have an impact on gas consumption. Because in smart contracts, there is a difference in the order of the functions. Each position will have an extra 22 gas. The order is depend on method ID, So if you rename the frequently accessed function to more early method ID, you can save gas cost.
Please see this site for further information.
8. Internal Function Calls is efficient
These function calls are translated into simple jumps inside the EVM. Just passing memory references to internally-called functions is very efficient.
So, moving some external call to internal improve efficiency.
9. Activate the optimizer
Before you deploy your contract, activate the optimizer when compiling using "solc --optimize --bin sourceFile.sol"
if you expect many transactions and do not care for higher deployment cost and output size, set --optimize-runs to a high number.
Please see the official site for further information.
10. Not use library always
Not using libraries when implementing the functionality is cheaper for simple usages. Calling library for a simple usages may be costly.
11. Compress input in smart contract
The basic gas for the transaction is 21,000. The input data is 68 gas per byte, and 4 gas if the byte is 0x00.
So, you can reduce gas cost by compressing input data.
Please see this site for further information.
This is quite interesting technique. If efficiency is critical, it's worth to do.
12. Short-circuiting rules
The operators || and && apply the common short-circuiting rules. This means that in the expression f(x) || g(y), if f(x) evaluates to true, g(y) will not be evaluated even if it may have side-effects.
So setting less costly function to f(x) and setting costly function to g(x) is efficient.
13. Optimize code by assembly
Inline assembly is also beneficial in cases where the optimizer fails to produce efficient code. Please see the examples of the official site.